package joc.internal.transformer;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import javassist.ClassPool;
import javassist.CtBehavior;
import javassist.CtClass;
import joc.customize.Contract;
import joc.customize.ForBehaviorCallAtBeginningOfBehavior;
import joc.customize.ForBehaviorEvaluateAtBeginningOfBehavior;
import joc.customize.ForBehaviorEvaluateAtEndOfBehavior;

public class GenericBehaviorTransformer extends DeclaredBehaviorTransformer {
	public GenericBehaviorTransformer(ClassPool pool) {
		super(pool);
	}

	@Override
	public void transform(CtClass clazz, CtBehavior behavior) throws Exception {
		logger.info("behavior " + behavior.getLongName());
		for (Class<? extends Annotation> contractAnnotation : Transformer.getContractMap().keySet()) {
			Annotation annotation = (Annotation) behavior.getAnnotation(contractAnnotation);
			if (annotation != null) {
				logger.info("behavior " + behavior.getLongName() + " has contract "
						+ Transformer.getContractMap().get(contractAnnotation).getClass().getName());
				applyContract(behavior, Transformer.getContractMap().get(contractAnnotation), annotation);
			}
		}
	}

	private void applyContract(CtBehavior behavior, Contract contract, Annotation annotation) throws Exception {
		for (Method contractMethod : getContractMethodsWithAnnotation(contract,
				ForBehaviorEvaluateAtBeginningOfBehavior.class)) {
			insertBefore(behavior, getContractCallString(behavior, true, contractMethod, contract, annotation, true));
			logger.info("inserted " + contract.getClass().getName() + " with " + contractMethod.getName()
					+ " in behavior " + behavior.getLongName());
		}
		for (Method contractMethod : getContractMethodsWithAnnotation(contract,
				ForBehaviorCallAtBeginningOfBehavior.class)) {
			insertBefore(behavior, getContractCallString(behavior, true, contractMethod, contract, annotation, false));
			logger.info("inserted " + contract.getClass().getName() + " with " + contractMethod.getName()
					+ " in behavior " + behavior.getLongName());
		}
		for (Method contractMethod : getContractMethodsWithAnnotation(contract,
				ForBehaviorEvaluateAtEndOfBehavior.class)) {
			insertAfter(behavior, getContractCallString(behavior, false, contractMethod, contract, annotation, true));
			logger.info("inserted " + contract.getClass().getName() + " with " + contractMethod.getName()
					+ " in behavior " + behavior.getLongName());
		}
	}

}
